#include "led.h"


#ifdef LED_DEBUGB
#define dbg_print(fmt, ...)			\
	do{								\
		printk("LED: ");			\
		printk(fmt, ##__VA_ARGS__);	\
		printk("\r\n");				\
	}while(0)
#else
#define dbg_print(fmt, ...) 
#endif

struct led_device{
	//寄存器虚拟地址
	void __iomem *MPU_AHB4_PERIPH_RCC_PI;
	void __iomem *GPIOI_MODER_PI;
	void __iomem *GPIOI_OTYPER_PI;
	void __iomem *GPIOI_OSPEEDR_PI;
	void __iomem *GPIOI_PUPDR_PI;
	void __iomem *GPIOI_BSRR_PI;

	//寄存器I/O内存资源，同一段IO内存只能申请一次，但是可以映射多次
	struct resource *MPU_AHB4_PERIPH_RCC_PI_resource;
	struct resource *GPIOI_MODER_PI_resource;
	struct resource *GPIOI_OTYPER_PI_resource;
	struct resource *GPIOI_OSPEEDR_PI_resource;
	struct resource *GPIOI_PUPDR_PI_resource;
	struct resource *GPIOI_BSRR_PI_resource;

	//设备号
	dev_t dev;
	//设备对象
	struct cdev cdev;
	//class对象
	struct class *cls;
	//led状态，0灭，1亮
	int led_state;
	//链表节点
	struct list_head node;
};

//定义并初始化链表头
static LIST_HEAD(led_list);

static int led_open(struct inode *inode, struct file *file);
static int led_release(struct inode *inode, struct file *file);
static ssize_t led_read(struct file *file, char __user *buf, size_t len, loff_t *pos);
static ssize_t led_write(struct file *file, const char __user *buf, size_t len, loff_t *pos);

static struct file_operations led_ops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.release = led_release,
	.read = led_read,
	.write = led_write,
};

void led_switch(struct led_device *led, uint8_t state)
{
	uint32_t val = 0;

	if(state)
	{
		val = readl(led->GPIOI_BSRR_PI);
		val |= (1 << 16);
		writel(val, led->GPIOI_BSRR_PI);
	}
	else
	{
		val = readl(led->GPIOI_BSRR_PI);
		val |= (1 << 0);
		writel(val, led->GPIOI_BSRR_PI);
	}	
}

static int led_open(struct inode *inode, struct file *file)
{
	struct led_device *led;
	
	led = container_of(inode->i_cdev, struct led_device, cdev);
	file->private_data = led;

	return 0;
}

static int led_release(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t led_read(struct file *file, char __user *buf, size_t len, loff_t *pos)
{
	char kernal_data;
	struct led_device *led = file->private_data;

	if(led->led_state == 0)
		kernal_data = '0';
	else if(led->led_state == 1)
		kernal_data = '1';
	else
		return -EINVAL;
	
	//将数据从内核空间拷贝到用户空间
	if(copy_to_user(buf, &kernal_data, 1))
		return -EFAULT;

	return 1;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t len, loff_t *pos)
{
	char user_data;
	struct led_device *led = file->private_data;
	
	//将数据从用户框架拷贝到内核框架
	if(copy_from_user(&user_data, buf, 1))
		return -EFAULT;

	if(user_data == '0')
	{
		led->led_state = 0;
		led_switch(led, 0);
	}
	else if(user_data == '1')
	{
		led->led_state = 1;
		led_switch(led, 1);
	}
	else
		return -EINVAL;

	return len;
}

//取消I/O内存映射
static void io_unmap(struct led_device *led)
{
	//取消I/O内存映射
	if(led->MPU_AHB4_PERIPH_RCC_PI)
	{
		iounmap(led->MPU_AHB4_PERIPH_RCC_PI);
		led->MPU_AHB4_PERIPH_RCC_PI = NULL;
	}
	if(led->GPIOI_MODER_PI)
	{
		iounmap(led->GPIOI_MODER_PI);
		led->GPIOI_MODER_PI = NULL;
	}
	if(led->GPIOI_OTYPER_PI)
	{
		iounmap(led->GPIOI_OTYPER_PI);
		led->GPIOI_OTYPER_PI = NULL;
	}
	if(led->GPIOI_OSPEEDR_PI)
	{
		iounmap(led->GPIOI_OSPEEDR_PI);
		led->GPIOI_OSPEEDR_PI = NULL;
	}
	if(led->GPIOI_PUPDR_PI)
	{
		iounmap(led->GPIOI_PUPDR_PI);
		led->GPIOI_PUPDR_PI = NULL;
	}
	if(led->GPIOI_BSRR_PI)
	{
		iounmap(led->GPIOI_BSRR_PI);
		led->GPIOI_BSRR_PI = NULL;
	}
	// //释放I/O内存资源
	// if(led->MPU_AHB4_PERIPH_RCC_PI_resource)
	// {
	// 	release_mem_region(RCC_MP_AHB4ENSETR, 4);
	// 	led->MPU_AHB4_PERIPH_RCC_PI_resource = NULL;
	// }
	// if(led->GPIOI_MODER_PI_resource)
	// {
	// 	release_mem_region(GPIOI_MODER, 4);
	// 	led->GPIOI_MODER_PI_resource = NULL;
	// }
	// if(led->GPIOI_OTYPER_PI_resource)
	// {
	// 	release_mem_region(GPIOI_OTYPER, 4);
	// 	led->GPIOI_OTYPER_PI_resource = NULL;
	// }
	// if(led->GPIOI_OSPEEDR_PI_resource)
	// {
	// 	release_mem_region(GPIOI_OSPEEDR, 4);
	// 	led->GPIOI_OSPEEDR_PI_resource = NULL;
	// }
	// if(led->GPIOI_PUPDR_PI_resource)
	// {
	// 	release_mem_region(GPIOI_PUPDR, 4);
	// 	led->GPIOI_PUPDR_PI_resource = NULL;
	// }
	// if(led->GPIOI_BSRR_PI_resource)
	// {
	// 	release_mem_region(GPIOI_BSRR, 4);
	// 	led->GPIOI_BSRR_PI_resource = NULL;
	// }
}

//进行I/O内存映射
static int io_map(struct led_device *led)
{
	// //申请I/O内存资源，在系统中I/O内存资源不可以被重复申请
	// led->MPU_AHB4_PERIPH_RCC_PI_resource = request_mem_region(RCC_MP_AHB4ENSETR, 4, "RCC_MP_AHB4ENSETR");
	// if(!led->MPU_AHB4_PERIPH_RCC_PI_resource)
	// {
	// 	io_unmap(led);
	// 	return -EIO;
	// }
	// led->GPIOI_MODER_PI_resource = request_mem_region(GPIOI_MODER, 4, "GPIOI_MODER");
	// if(!led->GPIOI_MODER_PI_resource)
	// {
	// 	io_unmap(led);
	// 	return -EIO;
	// }
	// led->GPIOI_OTYPER_PI_resource = request_mem_region(GPIOI_OTYPER, 4, "GPIOI_OTYPER");
	// if(!led->GPIOI_OTYPER_PI_resource)
	// {
	// 	io_unmap(led);
	// 	return -EIO;
	// }
	// led->GPIOI_OSPEEDR_PI_resource = request_mem_region(GPIOI_OSPEEDR, 4, "GPIOI_OSPEEDR");
	// if(!led->GPIOI_OSPEEDR_PI_resource)
	// {
	// 	io_unmap(led);
	// 	return -EIO;
	// }
	// led->GPIOI_PUPDR_PI_resource = request_mem_region(GPIOI_PUPDR, 4, "GPIOI_PUPDR");
	// if(!led->GPIOI_PUPDR_PI_resource)
	// {
	// 	io_unmap(led);
	// 	return -EIO;
	// }
	// led->GPIOI_BSRR_PI_resource = request_mem_region(GPIOI_BSRR, 4, "GPIOI_BSRR");
	// if(!led->GPIOI_BSRR_PI_resource)
	// {
	// 	io_unmap(led);
	// 	return -EIO;
	// }
	//进行I/O内存地址映射，在系统中I/O内存可以被重复映射
	led->MPU_AHB4_PERIPH_RCC_PI = ioremap(RCC_MP_AHB4ENSETR, 4);
	if(!led->MPU_AHB4_PERIPH_RCC_PI)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_MODER_PI = ioremap(GPIOI_MODER, 4);
	if(!led->GPIOI_MODER_PI)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_OTYPER_PI = ioremap(GPIOI_OTYPER, 4);
	if(!led->GPIOI_OTYPER_PI)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_OSPEEDR_PI = ioremap(GPIOI_OSPEEDR, 4);
	if(!led->GPIOI_OSPEEDR_PI)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_PUPDR_PI = ioremap(GPIOI_PUPDR, 4);
	if(!led->GPIOI_PUPDR_PI)
	{
		io_unmap(led);
		return -EIO;
	}
	led->GPIOI_BSRR_PI = ioremap(GPIOI_BSRR, 4);
	if(!led->GPIOI_BSRR_PI)
	{
		io_unmap(led);
		return -EIO;
	}

	return 0;
}

static void gpio_init(struct led_device *led)
{
	uint32_t val;
	
	/* 使能PI时钟 */
	val = readl(led->MPU_AHB4_PERIPH_RCC_PI);
	val |= (0X1 << 8);
	writel(val, led->MPU_AHB4_PERIPH_RCC_PI);
	/* 设置PI0通用的输出模式。*/
	val = readl(led->GPIOI_MODER_PI);
	val &= ~(0X3 << 0);
	val |= (0X1 << 0);
	writel(val, led->GPIOI_MODER_PI);
	/* 设置PI0为推挽模式。*/
	val = readl(led->GPIOI_OTYPER_PI);
	val &= ~(0X1 << 0);
	writel(val, led->GPIOI_OTYPER_PI);
	/* 设置PI0为高速。*/
	val = readl(led->GPIOI_OSPEEDR_PI);
	val &= ~(0X3 << 0);
	val |= (0x2 << 0);
	writel(val, led->GPIOI_OSPEEDR_PI);
	/* 设置PI0为上拉。*/
	val = readl(led->GPIOI_PUPDR_PI);
	val &= ~(0X3 << 0);
	val |= (0x1 << 0);
	writel(val, led->GPIOI_PUPDR_PI);
	/* 默认开启LED */
	led_switch(led, 1);
	led->led_state = 1;
}

static void gpio_deinit(struct led_device *led)
{
	uint32_t val;

	/* 关闭LED */
	led_switch(led, 0);
	led->led_state = 0;
	/* 设置PI0为悬空。*/
	val = readl(led->GPIOI_PUPDR_PI);
	val &= ~(0X3 << 0);
	writel(val, led->GPIOI_PUPDR_PI);
	/* 设置PI0通用的输入模式。*/
	val = readl(led->GPIOI_MODER_PI);
	val &= ~(0X3 << 0);
	writel(val, led->GPIOI_MODER_PI);
}

static int register_led(struct led_device *led)
{
	int result;
	struct device *device;

#ifndef USING_ALLOC_DEV_ID
	//注册设备ID
	led->dev = MKDEV(LED_MAJOR, LED_MINOR);
	result = register_chrdev_region(led->dev, 1, LED_NAME);
	if(result != 0)
		return result;
#else
	//动态分配设备ID
	result = alloc_chrdev_region(&led->dev, LED_MINOR, 1, LED_NAME);
	if(result != 0)
		return result;
#endif
	//初始化CDEV对象
	cdev_init(&led->cdev, &led_ops);
	led->cdev.owner = THIS_MODULE;

	//添加cdev对象到系统
	result = cdev_add(&led->cdev, led->dev, 1);
	if(result != 0)
	{
		unregister_chrdev_region(led->dev, 1);
		return result;
	}

	//自动创建设备文件
	led->cls = class_create(THIS_MODULE, LED_NAME);
	if(IS_ERR(led->cls))
	{
		cdev_del(&led->cdev);
		unregister_chrdev_region(led->dev, 1);
		return PTR_ERR(led->cls);
	}
	device = device_create(led->cls, NULL, led->dev, NULL, "myled");
	if(IS_ERR(device))
	{
		class_destroy(led->cls);
		cdev_del(&led->cdev);
		unregister_chrdev_region(led->dev, 1);
		return PTR_ERR(device);
	}

	printk("MAJOR %d,MINOR %d\r\n", MAJOR(led->dev), MINOR(led->dev));
	return 0;
}

static void unregister_led(struct led_device *led)
{
	//删除设备文件
	device_destroy(led->cls, led->dev);
	class_destroy(led->cls);
	//从内核删除cdev对象
	cdev_del(&led->cdev);
	//注销设备ID
	unregister_chrdev_region(led->dev, 1);
}

static int __init led_init(void)
{
	int result;
	struct led_device *led;

	printk("led_init\r\n");

	//分配led对象
	led = kzalloc(sizeof(struct led_device), GFP_KERNEL);
	if(led == NULL)
	{
		dbg_print("malloc mem failed\r\n");
		return -ENOMEM;
	}
	memset(led, 0, sizeof(struct led_device));

	//IO内存映射
	result = io_map(led);
	if(result != 0)
	{
		dbg_print("map io mem failed\r\n");
		kfree(led);
		return result;
	}

	//注册LED设备
	result = register_led(led);
	if(result != 0)
	{
		dbg_print("register led failed\r\n");
		io_unmap(led);
		kfree(led);
		return result;
	}

	//初始化LED设备
	gpio_init(led);

	/**
	 * 将LED对象放入链表尾
	 * new 欲插入的节点
	 * head 链表头
	 */
	list_add_tail(&led->node, &led_list);
	return 0;
}

static void __exit led_exit(void)
{
	struct led_device *led;
	struct list_head *p, *n;

	printk("led_exit\r\n");

	/** 
	 * 遍历链表
	 * pos 循环游标，指向当前所遍历到的节点
	 * n 用作临时存储，指向下一个即将遍历的节点
	 * head 链表头
	 */
	list_for_each_safe(p, n, &led_list)
	{
		//查找链表中的led对象
		led = list_entry(p, struct led_device, node);
		//从链表中移除LED对象
		list_del(&led->node);
		//反初始化GPIO
		gpio_deinit(led);
		//注销led设备
		unregister_led(led);
		//取消IO内存映射
		io_unmap(led);
		//释放LED对象
		kfree(led);
	}
}

module_init(led_init);
module_exit(led_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("LF");
MODULE_DESCRIPTION("led test");
